// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "asm.h"

.macro check_general_purpose reg
    cmpq $0, \reg
    jne .Lfail
.endm

.macro check_xmm reg
    // toss the register down on the stack in the red zone
    // so we can compare with zero.
    // avoids using the more obvious ptest instruction, which is
    // only present in SSE4.1
    movdqu \reg, -16(%rsp)
    cmpq $0, -16(%rsp)
    jnz .Lfail
    cmpq $0, -8(%rsp)
    jnz .Lfail
.endm

.macro check_ymm reg
    vptest \reg, \reg
    jnz .Lfail
.endm

.macro check_mm reg
    movq \reg, %rax
    cmpq $0, %rax
    jne .Lfail
.endm

.macro check_segment reg
    mov \reg, %ax
    cmpw $0, %ax
    jne .Lfail
.endm

# int thread_entry(void *arg)
FUNCTION(thread_entry)
    jz .Lfail
    jc .Lfail
    js .Lfail
    jp .Lfail

    check_general_purpose %rax
    check_general_purpose %rbx
    check_general_purpose %rcx
    check_general_purpose %rdx
    check_general_purpose %rbp
    check_general_purpose %rsi
    check_general_purpose %r8
    check_general_purpose %r9
    check_general_purpose %r10
    check_general_purpose %r11
    check_general_purpose %r12
    check_general_purpose %r13
    check_general_purpose %r14
    check_general_purpose %r15

    # test thread arg
    movq $0x1234567890abcdef, %rax
    cmpq %rax, %rdi
    jne .Lfail

    # Don't check cs/ss since those are set by the kernel explicitly
    check_segment %ds
    check_segment %es
    check_segment %fs
    check_segment %gs

    check_mm %mm0
    check_mm %mm1
    check_mm %mm2
    check_mm %mm3
    check_mm %mm4
    check_mm %mm5
    check_mm %mm6
    check_mm %mm7

    check_xmm %xmm0
    check_xmm %xmm1
    check_xmm %xmm2
    check_xmm %xmm3
    check_xmm %xmm4
    check_xmm %xmm5
    check_xmm %xmm6
    check_xmm %xmm7
    check_xmm %xmm8
    check_xmm %xmm9
    check_xmm %xmm10
    check_xmm %xmm11
    check_xmm %xmm12
    check_xmm %xmm13
    check_xmm %xmm14
    check_xmm %xmm15

    # Use Intel Vol 1 section 14.3 procedure for checking if AVX enabled

    mov $1, %eax
    cpuid
    # Check for OSXSAVE support (can we use xgetbv)
    test $(1<<27), %ecx
    jz .Lno_avx
    # Check for AVX support
    test $(1<<28), %ecx
    jz .Lno_avx
    # Check for operating system support for AVX
    mov $0, %ecx
    xgetbv
    and $0b110, %eax
    cmp $0b110, %eax
    jz .Lno_avx

    check_ymm %ymm0
    check_ymm %ymm1
    check_ymm %ymm2
    check_ymm %ymm3
    check_ymm %ymm4
    check_ymm %ymm5
    check_ymm %ymm6
    check_ymm %ymm7
    check_ymm %ymm8
    check_ymm %ymm9
    check_ymm %ymm10
    check_ymm %ymm11
    check_ymm %ymm12
    check_ymm %ymm13
    check_ymm %ymm14
    check_ymm %ymm15
.Lno_avx:

    jmp zx_thread_exit@PLT
.Lfail:
    jmp print_fail
